package cn.toolck.springmvc.spring.servlet;

import cn.toolck.springmvc.spring.annotation.MyRequestMapping;
import cn.toolck.springmvc.spring.annotation.MySecurity;
import cn.toolck.springmvc.spring.bean.BeanDefinition;
import cn.toolck.springmvc.spring.bean.HandlerMethod;
import cn.toolck.springmvc.spring.bean.SecurityHandler;
import cn.toolck.springmvc.spring.factory.BeanFactory;
import jdk.nashorn.internal.runtime.RewriteException;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * @ClassName MyDispatcherServlet
 * @Description 进行请求的分发
 * @Author Toolck Rin
 * @Date 2021/2/24 14:33
 * @Version 1.0
 **/
public class MyDispatcherServlet extends HttpServlet {
    private SecurityHandler securityHandler = new SecurityHandler();

    private Properties properties = new Properties();

    // 定义IoC容器
    private BeanFactory beanFactory = new BeanFactory();

    // handlerMapping
    // 存储url和Method之间的映射关系
    private Map<String, HandlerMethod> handlerMapping = new HashMap<>();


    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        initBeanFactory(servletConfig);
        insertHandlerMapping();
        initPostProcessor();
    }

    private void initPostProcessor() {
        securityHandler.dealWithSecurity(handlerMapping);
    }

    private void insertHandlerMapping() {
        Set<String> beanNames = beanFactory.getAllBeanName();
        beanNames.forEach(beanName -> {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            Class classType = beanDefinition.getClassType();
            Method[] methods = classType.getDeclaredMethods();
            String url = "";
            if (classType.isAnnotationPresent(MyRequestMapping.class)) {
                url += ((MyRequestMapping) classType.getAnnotation(MyRequestMapping.class)).value();
            }
            for (Method method : methods) {
                String handlerUrl = url;
                if (method.isAnnotationPresent(MyRequestMapping.class)) {
                    handlerUrl += ((MyRequestMapping) method.getAnnotation(MyRequestMapping.class)).value();
                    if (handlerMapping.containsKey(handlerUrl)) {
                        throw new RuntimeException("此路径重复的！");
                    }
                    handlerMapping.put(handlerUrl, new HandlerMethod(method, beanFactory.getBean(beanName)));
                }
            }
        });
    }

    public void initBeanFactory(ServletConfig servletConfig) {
        String contextConfigLocation = servletConfig.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        String property = properties.getProperty("package-scan");
        beanFactory.refesh(property);

    }

    private void doLoadConfig(String contextConfigLocation) {
        String prefix = "classpath:";
        if (contextConfigLocation.startsWith(prefix)) {
            contextConfigLocation = contextConfigLocation.substring(prefix.length());
        }
        InputStream contextConfigStream = MyDispatcherServlet.class.getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(contextConfigStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        HandlerMethod handlerMethod = handlerMapping.get(requestURI);
        if (handlerMethod == null) {
            writeEncodingUTF8(resp, requestURI + "-----此资源不存在！");
            return;
        }
        if (!securityHandler.hasPermissions(req)) {
            writeEncodingUTF8(resp, requestURI + "-----此资源访问需要权限，您不存在此权限！");
            return;
        }
        Method method = handlerMethod.getMethod();
        Object bean = handlerMethod.getBean();
        Object[] invokeParam = getParams(req, resp, method);
        try {
            Object invoke = method.invoke(bean, invokeParam);
            if (invoke instanceof String) {
                writeEncodingUTF8(resp, (String) invoke);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void writeEncodingUTF8(HttpServletResponse resp, String text) throws IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.getWriter().write(text);
    }

    private Object[] getParams(HttpServletRequest req, HttpServletResponse resp, Method method) {
        Map<String, String[]> parameterMap = req.getParameterMap();
        Parameter[] parameters = method.getParameters();
        Object[] param = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (parameter.getType().equals(HttpServletRequest.class)) {
                param[i] = req;
            } else if (parameter.getType().equals(HttpServletResponse.class)) {
                param[i] = resp;
            } else {
                String name = parameter.getName();
                String[] strings = parameterMap.get(name);
                param[i] = StringUtils.join(strings, ",");
            }
        }
        return param;
    }

}
